
using Progress.Lang.*.

using OpenEdge.WebSpeed.WebContext from propath.
using OpenEdge.WebSpeed.Response from propath.
using OpenEdge.WebSpeed.Container from propath.
using OpenEdge.WebSpeed.Session from propath.

routine-level on error undo, throw.

class OpenEdge.WebSpeed.Request: 

    define private variable m_actionalFilter     as OpenEdge.WebSpeed.Filters.Actional no-undo.
    
    define private variable m_serverConnectionId as character                          no-undo.
    define private variable m_usettWebFieldList  as logical                            no-undo.
    
    define private variable CgiVar               as character                          no-undo extent 255.
    define private variable CgiList              as character                          no-undo.
        
    define public property container            as Container                          no-undo
        get.
        private set.
    
    define temp-table ttWebFieldList no-undo
        field field-num  as integer
        field field-name as character
        field field-type as character /* 'F' or 'Q' */
        index prim-index is primary field-name field-type field-num.    

    constructor public Request(container as Container):
        this-object:Container = container.
        this-object:WebContext = container:WebContext.
    end constructor.
    
    destructor Request():
        this-object:Container = ?.
        this-object:WebContext = ?.
        this-object:m_actionalFilter = ?.
    end.
    
    /**
      @private Don't override this method.  It is intended to be called
      from the framework 
    */
    method public void InitRequest(input response as Response, webSession as Session):
        
        this-object:Session = webSession.
        this-object:Response = response.
        this-object:InitVariables().
        this-object:InitCGI().
        this-object:InitFilters().
        
    end method.
    
    method public void EndRequest():
        this-object:EndFilters().
    end.
    
    method protected void InitCGI():
		    
        define variable v-http-host as character no-undo.
        define variable v-host      as character no-undo.
        define variable v-port      as character no-undo.
		
		
        this-object:ServerConnectionId = session:server-connection-id.
		  
        /* Set global variables HostURL, AppURL and SelfURL so self-referencing
           URL's can be generated by applications. If the Host: header (HTTP_HOST) 
           was sent by the browser, using it will provide for fewer problems with 
           self-referencing URL's than SERVER_NAME and SERVER_PORT. */
        v-http-host = this-object:get-cgi("HTTP_HOST":U).
		    
        if v-http-host = "" then 
        do:
            /* No Host: header was sent by the browser. */
            v-host = this-object:ServerName.
            v-port = this-object:ServerPort.
        end.
		      
        else if num-entries(v-http-host, ":":U) = 2 then 
            do:
                /* Host: hostname:port combination was sent by the browser */
                v-host = entry(1, v-http-host, ":":U).
                v-port = entry(2, v-http-host, ":":U).
            end.
            else 
            do:
                /* Else Host: hostname with no port number was sent by the browser */
                v-host = v-http-host.
                v-port = this-object:ServerPort.
            end.
		      
        /* Set the scheme, host and port of the URL to ourself.  Omit
           port if 80 or 443 if https is on. */
        if this-object:HTTPS = "ON":U then 
        do:
            this-object:HostURL = (if v-host = "" then ""
            else "https://":U + v-host +
                (if v-port = "443":U then "" else ":":U + v-port)).
        end. 
        else 
        do:
            this-object:HostURL = (if v-host = "" then ""
            else "http://":U + v-host +
                (if v-port = "80":U then "" else ":":U + v-port)).
        end.
		
        /* Server-relative URL to ourself (this program) except for optional
             QUERY_STRING. */
        this-object:SelfURL = this-object:ScriptName + this-object:PathInfo.
		
        /* Check for alternate URL format used by the Messengers */
        if this-object:PathInfo begins "/WService=":U then 
        do:
            /* Web object filename is everything after the second "/" in PATH_INFO */
            this-object:AppProgram = (if num-entries(this-object:PathInfo, "/":U) >= 3 then
                substring(this-object:PathInfo, index(this-object:PathInfo, "/":U, 2) + 1)
                else "").
            /* Server relative URL of this Web objects's application */
            this-object:ApplicationURL     = this-object:ScriptName + "/":U + entry(2, this-object:PathInfo, "/":U).
		
        end. 
        else 
        do:
            /* Web object filename is everything after the second "/" in PATH_INFO */
            this-object:AppProgram = substring(this-object:PathInfo, 2).
            /* Server relative URL of this Web objects's application */
            this-object:ApplicationURL     = this-object:ScriptName.
        end.
		
        /* If the ApplicationURL option was set in the Windows Registry or
           webspeed.cnf, then use that to set AppURL instead of SCRIPT_NAME and
           PATH_INFO.  Make sure it's prefixed with a "/" since we don't handle
           an entire URL. */
		     
        if this-object:Container:ApplicationURL begins "/":U then 
        do:
            this-object:ApplicationURL  = this-object:Container:ApplicationURL.
            this-object:SelfURL = this-object:ApplicationURL + "/":U + this-object:AppProgram.
        end.
		
        /* The Alibaba 2.0 NT server upper cases SCRIPT_NAME and PATH_INFO.  This
           is a bug.  To work around this, lower case AppURL, etc.  Otherwise
           Cookies (which are case sensitive) will fail to match preventing
           locking from working . */
        if this-object:ServerSoftware begins "Alibaba/2":U then 
        do:
            this-object:HostURL    = lc(this-object:HostURL).
            this-object:ApplicationURL     = lc(this-object:ApplicationURL).
            this-object:SelfURL    = lc(this-object:SelfURL).
            this-object:AppProgram = lc(this-object:AppProgram).
        end.
		 
        this-object:HTTPNewLine             = (if this-object:ServerSoftware begins "Netscape-":U 
            or this-object:ServerSoftware begins "IPlanet-":U 
            or this-object:ServerSoftware begins "Sun ONE":U 
            or this-object:ServerSoftware begins "FrontPage-PWS":U then "~n":U
            else "~r~n":U).
		
        /* Set cookie defaults from either configuration defaults or AppURL */
        this-object:CookiePath   = (if this-object:Container:CookiePath <> "" then this-object:Container:CookiePath else this-object:ApplicationURL).
        this-object:CookieDomain = this-object:Container:CookieDomain.    
    
    end.

    /**
      Purpose:     Load field list from form and query into temp-table for quick
                   access. (when form has many fields, lookups are slow)
      Parameters:  <none>
      Notes: 	
	
    */
    
    method protected void populateWebFieldListTable():
	        
        define variable i       as integer   no-undo.
        define variable j       as integer   no-undo.
        define variable iTotal  as integer   no-undo.
        define variable v-str   as character no-undo.
        define variable v-tmp   as character no-undo.
        define variable v-lng   as longchar  no-undo.
        define variable useLong as logical   no-undo.
	
        m_usettWebFieldList = no.
	
        assign 
            v-str = this-object:WebContext:get-cgi-list("FORM":U)
            j     = num-entries(v-str) NO-ERROR.
	
        /* if we failed with a char, number of fields must be too big,
           so try a longchar 
        */
        if error-status:error then 
        do:
            assign 
                v-lng   = this-object:WebContext:get-cgi-list("FORM":U)
                j       = num-entries(v-lng)
                useLong = yes.
        end.
	
        /* if not more than 100 fields, temp-table may be slower */
        if j <= 100 then
            return.
	
        define buffer ttWebFieldList for temp-table ttWebFieldList.
	
        do i = 1 to j:
            create ttWebFieldList.
            assign 
                iTotal                    = iTotal + 1
                v-tmp                     = (if not useLong then entry(i, v-str) else entry(i, v-lng))
                ttWebFieldList.field-num  = iTotal
                ttWebFieldList.field-name = v-tmp
                ttWebFieldList.field-type = 'F':U.
        end.
	
        assign 
            v-str = this-object:WebContext:get-cgi-list("QUERY":U)
            j     = num-entries(v-str).
	
        do i = 1 to j:
            create ttWebFieldList.
            assign 
                iTotal                    = iTotal + 1
                ttWebFieldList.field-num  = iTotal
                ttWebFieldList.field-name = entry(i, v-str)
                ttWebFieldList.field-type = 'Q':U.
        end.
	
        m_usettWebFieldList = yes.
	        
    end. 
    
    method protected void InitVariables():
		    
        define variable i-field as character no-undo.
        define variable i-pair  as character no-undo.
        define variable asc-del as character no-undo
            initial "~377":U.   /* delimiter character in octal = CHR(255) */
        define variable hex-del as character no-undo
            initial "%FF":U.    /* delimiter character in encoded hex */
        define variable ix      as integer   no-undo.
        define variable eql     as integer   no-undo.
	  
	
        this-object:populateWebFieldListTable().

        define variable i as integer no-undo.
        
        i = num-entries(this-object:WebContext:current-environment, asc-del).   
        
        do ix = 1 to i:
            i-pair     = entry(ix, this-object:WebContext:current-environment, asc-del).
            eql        = index(i-pair,"=":U).
            i-field    = substring(i-pair,1,eql - 1,"RAW":U).
            this-object:CgiVar[ix] = replace(substring(i-pair,eql + 1,-1,"RAW":U),hex-del,asc-del).
            this-object:CgiList    = CgiList + (if CgiList = "" then "" else ",":U ) + i-field.
        end.
	
        /* Import CGI 1.1 variables into global variables */
        this-object:AuthenticationType     = this-object:get-cgi("AUTH_TYPE":U).
        this-object:ContentLength  		   = integer(this-object:get-cgi("CONTENT_LENGTH":U)).
        this-object:ContentType            = this-object:get-cgi("CONTENT_TYPE":U).
        this-object:GatewayInterface       = this-object:get-cgi("GATEWAY_INTERFACE":U).
        this-object:PathInfo               = this-object:get-cgi("PATH_INFO":U).
        this-object:PathTranslated         = this-object:get-cgi("PATH_TRANSLATED":U).
        this-object:QueryString            = this-object:get-cgi("QUERY_STRING":U).
        this-object:RemoteAddress          = this-object:get-cgi("REMOTE_ADDR":U).
        this-object:RemoteHost             = this-object:get-cgi("REMOTE_HOST":U).
        this-object:RemoteIdentification   = this-object:get-cgi("REMOTE_IDENT":U).
        this-object:RemoteUser             = this-object:get-cgi("REMOTE_USER":U).
        this-object:RequestMethod          = this-object:get-cgi("REQUEST_METHOD":U).
        this-object:ScriptName             = this-object:get-cgi("SCRIPT_NAME":U).
        this-object:ServerProtocol         = this-object:get-cgi("SERVER_PROTOCOL":U).
        this-object:ServerName             = this-object:get-cgi("SERVER_NAME":U).
        this-object:ServerPort             = this-object:get-cgi("SERVER_PORT":U).
        this-object:ServerSoftware         = this-object:get-cgi("SERVER_SOFTWARE":U).
        this-object:HTTPAccept             = this-object:get-cgi("HTTP_ACCEPT":U).
        this-object:HTTPCookie             = this-object:get-cgi("HTTP_COOKIE":U).
        this-object:HTTPReferer            = this-object:get-cgi("HTTP_REFERER":U).
        this-object:HTTPUserAgent          = this-object:get-cgi("HTTP_USER_AGENT":U).
        this-object:HTTPS                  = this-object:get-cgi("HTTPS":U).
	
        /* Test for Microsoft's IIS which doesn't use HTTPS ON/OFF*/
        if this-object:ServerSoftware begins "Microsoft-IIS/":U and
            this-object:get-cgi("SERVER_PORT_SECURE":U) = "1":U then 
        do:
            this-object:HTTPS = "ON":U.
        end.
	
        /* Other environment variables */   
        this-object:UTCOffset = this-object:WebContext:utc-offset.
	
        /* If SERVER_PORT is null, then set it 80 or 443 if HTTPS is ON */
        if this-object:ServerPort = "" then 
        do:
            this-object:ServerPort = (if this-object:HTTPS = "ON":U then "443":U else "80":U).
        end.
    
    end.
    
    
    method protected void InitFilters():
    
        if valid-object(m_actionalFilter) then 
        do:
            m_actionalFilter:InitRequest(this-object).
        end.
    
    end method.
    
    method public void EndFilters():

        if valid-object(m_actionalFilter) then 
        do:
            m_actionalFilter:EndRequest(this-object).
        end.

    end.
    
    define public property Response as Response no-undo
        get.
        private set.
    
    define public property RequestMethod as character no-undo init "":U
        get.
        private set.
	
    define public property GatewayInterface as character no-undo init "":U
        get.
        private set.
	
    define public property ServerSoftware as character no-undo  init "":U
        get.
        private set.
	
    define public property ServerProtocol as character no-undo  init "":U
        get.
        private set.
	
    define public property ServerName as character no-undo  init "":U
        get.
        private set.
    
    define public property ServerPort as character no-undo  init "":U
        get.
        private set.
    
    define public property PathInfo as character no-undo  init "":U
        get.
        private set.
        
    define public property ScriptName as character no-undo init "":U
        get.
        private set.
    
    define public property PathTranslated as character no-undo  init "":U
        get.
        private set.
    
    define public property QueryString as character no-undo  init "":U
        get.
        private set.    
    
    define public property RemoteAddress as character no-undo  init "":U
        get.
        private set.    
    
    define public property RemoteHost as character no-undo  init "":U
        get.
        private set.    
	
    define public property RemoteIdentification as character no-undo  init "":U
        get.
        private set.    
	
    define public property RemoteUser as character no-undo  init "":U
        get.
        private set.    
	
    define public property AuthenticationType as character no-undo  init "":U
        get.
        private set.    
	 
    define public property ContentType as character no-undo init "":U  
        get.
        private set.    

    define public property ContentLength as integer  init 0
        get.
        private set.    

    define public property HTTPAccept as character no-undo  init "":U
        get.
        private set.    

    define public property HTTPCookie as character no-undo  init "":U
        get.
        private set.    

    define public property HTTPReferer as character no-undo  init "":U
        get.
        private set.
        
    define public property HTTPUserAgent as character no-undo init "":U
        get.
        private set.    

    define public property HTTPS as character no-undo  init "":U
        get.
        private set.  
        
    define public property HTTPNewLine as character no-undo init "":U
        get.
        public set.  

    define public property AppProgram as character no-undo init "":U 
        get.
        private set.    

    define public property ApplicationURL as character no-undo  init "":U
        get.
        private set.    

    define public property SelfURL as character no-undo  init "":U
        get.
        private set.    

    define public property HostURL as character no-undo  init "":U
        get.
        private set.    

    define public property CookiePath as character no-undo  init "":U
        get.
        private set.    

    define public property UseConnectionID as character no-undo  init "":U
        get.
        private set.    


    define public property CookieDomain as character no-undo init "":U
        get.
        private set. 

    define public property FieldDelimiter as character no-undo init ",":U
        get.
        private set. 

    define public property UTCOffset as integer no-undo init -1
        get.
        private set. 

    define public property WebContext as WebContext no-undo
        get.
        private set.
      
    define public property UserFieldVar as character init "":U extent 255
        get.
        public set.
      
    define public property UserFieldList as character init "":U
        get.
        public set.        
        
    define public property SelDelim as character init "":U
        get.
        public set.
      
    define public property FieldList as character init "":U
        get.
        public set.
      
    define public property FieldVar as character init "":U
        get.
        public set.   
        
    define public property Session as Session no-undo
        get.
        private set. 
        
        
    define public property ServerConnectionId as character 
        get():
            return m_serverConnectionId.
        end.
        private set(input connectionId as character):
            m_serverConnectionId = connectionId.

		
            if this-object:UseConnectionId = "0":U then 
            do:
                /* Delete the SERVER_CONNECTION_ID cookie */
                if index(this-object:HTTPCookie,"SERVER_CONNECTION_ID=":U) > 0 then 
                do:
                    this-object:Response:DeleteCookie("SERVER_CONNECTION_ID", ?, ?).
                end.
		    
                /* Let core know the logical session has ended */
                this-object:WebContext:session-end = true.
            end.
            else if connectionId <> "" then 
                do:
                    /* Create the SERVER_CONNECTION_ID cookie used to maintain context across 
                       browser sessions. */
                    this-object:Response:SetCookie("SERVER_CONNECTION_ID", connectionId, ?, ?, ?, ?, ?).
		    
                    /* Let core know the logical session is active */
                    this-object:WebContext:session-end = false.
                end.      
      
        end.
     
    method public character get-cgi(input cgiVar as character).
      
        define variable v-value as character no-undo.
        define variable i       as integer   no-undo.
		  
        if cgiVar  = ? then
            return this-object:WebContext:get-cgi-list().
        else
            return this-object:WebContext:get-cgi-value(cgiVar).      
      
    end.

    /**
       Performs conversions of date and time between local time and
         UTC time.  In addition, an option is supported to normalize a date and
         time ensuring the value of time is legal between zero and the number of
         seconds per day.  The normalizing step is also performed for either the
         local to UTC or UTC to local conversions.
       Parameters:
         Input:  Conversion option:
                 "UTC" - Converts date and time from local to UTC time.
                 "LOCAL" - Converts date and time from UTC to local time.
                 "NORMALIZE" - Normalize date and time so the value of time is
                   legal between zero and the number of seconds per day.
         Input:  Date to convert.  Uses the DATE data type.
         Input:  Time to convert.  Seconds since midnight (see TIME function).
         Output: Converted date.
         Output: Converted time.
       Returns: 
       Global Variables: utc-offset	
	
    */


    method public character ConvertDateTime 
        (input p_conversion as character,
        input p_idate       as date,
        input p_itime       as integer,
        output p_odate      as date,
        output p_otime      as integer):


        define variable seconds-per-day as integer no-undo initial 86400.

        /* Default option is to normalize */
        if p_conversion = "" or p_conversion = ? then
            assign p_conversion = "NORMALIZE":U.

        /* If date is ? ... */
        if p_idate = ? then
            return "".

        if p_itime = ? then
            assign p_itime = 0.

        /* Set time adjustment depending on conversion option */
        case p_conversion:
            when "LOCAL":U then
                p_itime = p_itime - this-object:utcoffset.
            when "UTC":U then
                p_itime = p_itime + this-object:utcoffset.
        end case.

        /* Normalize if time is too large */
        do while p_itime >= seconds-per-day:
            assign
                p_itime = p_itime - seconds-per-day
                p_idate = p_idate + 1.  /* tomorrow */
        end.

        /* Normalize if time is too small */
        do while p_itime < 0:
            assign
                p_itime = p_itime + seconds-per-day
                p_idate = p_idate - 1.  /* yesterday */
        end.

        assign
            p_odate = p_idate
            p_otime = p_itime.

        return "".

    end method. /* convert-datetime */


    /**
	
            Returns a date and time string formatted for Internet use.
          Currently, the formats "COOKIE" and "HTTP" are supported.  The HTTP format
          is useful for the Expires: or other headers.  The Cookie format is used
          by the set-cookie() function when an expiration date is specified.
        Parameters:
          Input:  Date format.  Supported are "COOKIE" and "HTTP".
          Input:  Date as a DATE data type.
          Input:  Time as an integer (See TIME function).
          Input:  Options.  "LOCAL" - indicates the specified date and time are local
                  time such as returned by the TODAY and TIME functions.  The date
                  and time are converted to UTC before formatting.
                  "UTC" - The specified date and time are already in UTC time.  The
                  date and time are normalized to ensure the value of time is between
                  zero and the number of seconds in one day.
        Returns:  Formatted date
        Global variables:
        References:
          Cookie Date format:
            Netscape Cookie Spec: http://home.netscape.com/newsref/std/cookie_spec.html
          HTTP Date format:
            RFC 2068, 3.3 Date/Time Formats: http://ds.internic.net/rfc/rfc2068.txt
	
    */
    method public character format-datetime 
        (input p_format  as character,
        input p_date    as date,
        input p_time    as integer,
        input p_options as character) :

        define variable p_rfcdate    as character no-undo.

        /* Does RFC 850/822 allow translated days and months?  Think not. */
        define variable weekday-list as character no-undo initial
            "Sun,Mon,Tue,Wed,Thu,Fri,Sat":U.
        define variable month-list   as character no-undo initial
            "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec":U.

        /* If date is ?, return a blank date */
        if p_date = ? then
            return "".

        if p_time = ? then
            assign p_time = 0.

        /* If no options are specified, LOCAL is the default */
        if p_options = "" or p_options = ? then
            assign p_options = "LOCAL":U.

        /* If Local was specified and the format is Cookie or HTTP, convert date and 
           time to UTC. */
        if can-do(p_options, "LOCAL":U) and
            (p_format = "COOKIE":U or p_format = "HTTP":U) then
            /* Convert date and time from Local to UTC */
            convert-datetime("UTC":U, p_date, p_time, output p_date, output p_time).
        /* Otherwise, just normalize */
        else
            /* Normalize date and time */
            convert-datetime("NORMALIZE":U, p_date, p_time, output p_date, output p_time).

        /* Output the formatted date */
        case p_format:
            when "COOKIE":U then 
                do:
                    /* Cookie format based on RFC-1123: Wdy, DD-Mon-YYYY HH:MM:SS GMT */
                    assign 
                        p_rfcdate = entry(weekday(p_date), weekday-list) + ", ":U +
                    STRING(day(p_date),"99":U) + "-":U +
                    entry(month(p_date), month-list) + "-":U +
                    STRING(year(p_date), "9999":U) + " ":U +
                    STRING(p_time,"HH:MM:SS":U) + " GMT":U.
                end.
            when "HTTP":U then 
                do:
                    /* HTTP format based on RFC-1123: Wdy, DD Mon YYYY HH:MM:SS GMT */
                    assign 
                        p_rfcdate = entry(weekday(p_date), weekday-list) + ", ":U +
                    STRING(day(p_date),"99":U) + " ":U +
                    entry(month(p_date), month-list) + " ":U +
                    STRING(year(p_date), "9999":U) + " ":U +
                    STRING(p_time,"HH:MM:SS":U) + " GMT":U.
                end.
            otherwise 
            do:
                undo, throw new Progress.Lang.AppError("format-datetime: ":U + "format '" + p_format +
                    "' is not supported").
            end.
        end case.

        return p_rfcdate.

    end.   
    /**
    Performs conversions of date and time between local time and
              UTC time.  In addition, an option is supported to normalize a date and
              time ensuring the value of time is legal between zero and the number of
              seconds per day.  The normalizing step is also performed for either the
              local to UTC or UTC to local conversions.
            Parameters:
              Input:  Conversion option:
                      "UTC" - Converts date and time from local to UTC time.
                      "LOCAL" - Converts date and time from UTC to local time.
                      "NORMALIZE" - Normalize date and time so the value of time is
                        legal between zero and the number of seconds per day.
              Input:  Date to convert.  Uses the DATE data type.
              Input:  Time to convert.  Seconds since midnight (see TIME function).
              Output: Converted date.
              Output: Converted time.
            Returns: 
            Global Variables: utc-offset    
    
    */
    method public character convert-datetime
        (input p_conversion as character,
        input p_idate       as date,
        input p_itime       as integer,
        output p_odate      as date,
        output p_otime      as integer) :
        define variable seconds-per-day as integer no-undo initial 86400.

        /* Default option is to normalize */
        if p_conversion = "" or p_conversion = ? then
            p_conversion = "NORMALIZE":U.

        /* If date is ? ... */
        if p_idate = ? then
            return "".

        if p_itime = ? then
            p_itime = 0.

        /* Set time adjustment depending on conversion option */
        case p_conversion:
            when "LOCAL":U then
                p_itime = p_itime - this-object:UTCOffset.
            when "UTC":U then
                p_itime = p_itime + this-object:UTCOffset.
        end case.

        /* Normalize if time is too large */
        do while p_itime >= seconds-per-day:
            p_itime = p_itime - seconds-per-day.
            p_idate = p_idate + 1.  /* tomorrow */
        end.

        /* Normalize if time is too small */
        do while p_itime < 0:
            p_itime = p_itime + seconds-per-day.
            p_idate = p_idate - 1.  /* yesterday */
        end.

        p_odate = p_idate.
        p_otime = p_itime.

        return "".

    end.
	
    /**
	
    Retrieves a MEMPTR with the contents of a file posted with a
                 multipart/form-data form
    Input Parameter: Name of variable (a field of type 'file' from the form)
    Returns: MEMPTR or ? (which means the file was too big). 
	
    */
	
    method public memptr get-binary-data
        (input p_name as character) :
        return this-object:WebContext:get-binary-data(p_name).
    end.
	
	
    /**
    Retrieves the LONGCHAR value for the specified CGI variable
    Input Parameter: Name of variable or ?
    Returns: Value or blank if invalid name.
	
    */
    method public longchar get-cgi-long
        (input p_name as character) :
        if p_name = ? then
            return this-object:WebContext:get-cgi-list("ENV":U).
        else
            return this-object:WebContext:GET-CGI-LONG-VALUE(p_name).
	
    end.
	
    /**
	
    Retrieves the associated value for the specified form field.
    Input Parameter: Name of field or ?
    Returns: Value of field or blank if invalid field name.  If ? was
      specified for the name, the list of fields is returned.
    Global Variables: FieldList
	
    */
	
    method public character get-field
        (input p_name as character) :
        define variable v-value as character no-undo.
        define variable i       as integer   no-undo.
        define variable j       as integer   no-undo.
	
        define variable v-form  as character no-undo.
        define variable v-query as character no-undo.
        define variable v-name  as character no-undo.
        define variable cTmp    as character no-undo.
	
        /* Get list of fields? */
        if p_name = ? then 
        do:
            /* If the field list was already computed for this request, return it */
            if FieldList <> "" then
                return FieldList.
	    
            assign 
                v-form  = this-object:WebContext:get-cgi-list("FORM":U)
                v-query = this-object:WebContext:get-cgi-list("QUERY":U)
                /* Combine form input and query string */
                v-value = v-form +
	             (if v-form <> "" and v-query <> "" then ",":U else "") +
	             v-query
                j       = num-entries(v-value).
	
            /* If returning a field list, eliminate dupes */
            do i = 1 to j:
                assign 
                    v-name = entry(i, v-value).
                if lookup(v-name, cTmp) = 0 then
                    assign cTmp = cTmp +
	               (if cTmp = "" then "" else ",":U) + v-name.
            end.
	
            assign 
                FieldList = cTmp. /* save it away */
	
            return FieldList.
        end.
	
        /* Else, get a field value */
        else 
        do:
            /* Return the output directly to maximize the allowable length.
               Replace all CF/LF's with with an LF so when an HTML <TEXTAREA>
               is saved in a database, etc. it won't contain extra characters
               or double-space output. */
	
            define variable getFromForm as logical no-undo.
	
            if m_usettWebFieldList then
                getFromForm = can-find (first ttWebFieldList where field-name = p_name
                    and field-type = "F":U).
            else 
                getFromForm = lookup(p_name, this-object:WebContext:get-cgi-list("FORM":U)) > 0.
	
            if getFromForm then
                return replace(this-object:WebContext:get-cgi-value("FORM":U, p_name, SelDelim),
                    "~r~n":U, "~n":U).
            else
                return replace(this-object:WebContext:get-cgi-value("QUERY":U, p_name, SelDelim),
                    "~r~n":U, "~n":U).
        end.
        return v-value.
    end.
	
	
    /**
	
    Retrieves the associated value for the specified form field.
    Input Parameter: Name of field or ?
    Returns: Value of field or blank if invalid field name.  If ? was
      specified for the name, the list of fields is returned.
	
    Similar to get-field but handles long list of fields with LONGCHAR
	
    Global Variables: FieldList
	
    */
    method public longchar get-fieldEx 
        (input p_name as character) :
        define variable v-value as longchar  no-undo.
        define variable i       as integer   no-undo.
        define variable j       as integer   no-undo.
	
        define variable v-form  as longchar  no-undo.
        define variable v-query as longchar  no-undo.
        define variable v-name  as character no-undo.
        define variable v-ret   as longchar  no-undo.
	
        /* Get list of fields? */
        if p_name = ? then 
        do:
	
            v-form  = this-object:WebContext:get-cgi-list("FORM":U).
            v-query = this-object:WebContext:get-cgi-list("QUERY":U).
            /* Combine form input and query string */
            v-value = v-form +
                (if v-form <> "" and v-query <> "" then ",":U else "") +
                v-query.
            j       = num-entries(v-value).
	
            /* If returning a field list, eliminate dupes */
            do i = 1 to j:
                assign 
                    v-name = entry(i, v-value).
                if lookup(v-name, v-ret) = 0 then
                    assign v-ret = v-ret +
	               (if v-ret = "" then "" else ",":U) + v-name.
            end.
	
            return v-ret.
        end.
	
        /* Else, get a field value */
        else 
        do:
            define variable cTmp as character no-undo.
	
            /* Return the output directly to maximize the allowable length.
               Replace all CF/LF's with with an LF so when an HTML <TEXTAREA>
               is saved in a database, etc. it won't contain extra characters
               or double-space output. */
            if get-from-form-fields(p_name) ne no /* yes and ? should go here */ then
                cTmp = this-object:WebContext:GET-CGI-VALUE("FORM":U, p_name, SelDelim).
            else
                cTmp = this-object:WebContext:GET-CGI-VALUE("QUERY":U, p_name, SelDelim).
	
            if (cTmp > "") then
                cTmp = replace(cTmp, "~r~n":U, "~n":U).
	  
            return cTmp. /* using char for performance */
	
        end.
	  
    end.
	
	
    /**
	
    Checks if field exists in the form.
	
    */
    method public logical get-from-form-fields
        (input p_name as character) :
        define variable getFromForm as logical no-undo.
	
        if m_usettWebFieldList then
            getFromForm = can-find (first ttWebFieldList where field-name = p_name
                and field-type = "F":U).
        else
            getFromForm = lookup(p_name, this-object:WebContext:get-cgi-list("FORM":U)) > 0.
	
        return getFromForm.
	
    end.
	
    /**
	
    Retrieves the longchar value for a field or cookie.
    Input Parameter: Name of field
    Returns: Value of form field or Cookie in that order or blank if an invalid name. 
	
    */
	
    method public longchar get-long-value
        (input p_name as character) :
        define variable cValue as longchar no-undo.
        define variable i      as integer  no-undo.
	
        /* If name is ? return blank */
        if p_name = ? or p_name = "" then 
            return cValue.
	
        /* item name passed so look fields and query string. */
        if get-from-form-fields(p_name) ne no /* yes and ? should go here */ then
            cValue = this-object:WebContext:get-cgi-long-value("FORM":U, p_name, SelDelim).
        else
            cValue = this-object:WebContext:get-cgi-long-value("QUERY":U, p_name, SelDelim).
	
        if (cValue > "") then
            cValue = replace(cValue, "~r~n":U, "~n":U).
	  
        return cValue.
	
    end.
	

    /**
	
    Sets the associated value for the specified user field.  User
      field values are global and available to any Web object run by the
      current Agent in the same web request.  The value can be retrieved with 
      get-user-field() or get-value(). 
    Input Parameters: Name of user field, associated value
    Returns: TRUE if field added, otherwise FALSE
    Side effects: Queues a message if adding a field fails
    Global Variables: UserFieldList, UserFieldVar
	
    */    
    method public logical set-user-field
        (input p_name as character,
        input p_value as character) :
	
        define variable i as integer no-undo.
	
        assign 
            i = lookup(p_name, UserFieldList).
	
        if i > 0 then
            UserFieldVar[i] = p_value.
        else 
        do:
            if num-entries(UserFieldList) < 255 then
                assign
                    UserFieldList   = UserFieldList +
	          (if UserFieldList = "" then "" else ",":U) + p_name
                    i               = num-entries(UserFieldList)
                    UserFieldVar[i] = p_value.
            else 
            do on error undo, throw:
                undo, throw new Progress.Lang.AppError("maximum number of user field entries 255 exceeded").
            end.
        end.
        return (i <> ?).  /* return TRUE unless field could not be added */
    end.	
	
    /**
	
    Retrieves the associated value for the specified user field
      that was set with set-user-field().
    Input Parameter: Name of user field or ?
    Returns: Value of user field or blank if invalid name.  If ? was
      specified for the name, the list of user fields is returned.
    Global Variables: UserFieldList, UserFieldVar
	
    */
    method public character get-user-field
        (input p_name as character) :
        define variable i as integer no-undo.
	
        if p_name = ? then
            return UserFieldList.
        else 
        do:
            assign 
                i = lookup(p_name, UserFieldList).
            return (if i > 0 then UserFieldVar[i] else "").
        end.
    end.
	
	
    /**
    Retrieves the first available value for a user field, field
      or cookie.
    Input Parameter: Name of item or ?
    Returns: Value of user field, form field or Cookie in that order or blank if 
      an invalid name.  If ? was specified for the name, a comma separated list
    of all user fields, fields and cookies is returned.
    Global Variables: UserFieldList, UserFieldVar, FieldList, FieldVar
	
    */
    method public character get-value 
        (input p_name as character) :
        define variable v-value       as character no-undo.
        define variable v-field-list  as character no-undo.
        define variable v-cookie-list as character no-undo.
        define variable i             as integer   no-undo.
        define variable found         as logical   no-undo.
	
        /* If name is ?, pass a list of all names in user fields, fields and
           cookies. */
        if p_name = ? then 
        do:
            v-field-list  = get-field(?).
            v-value       = UserFieldList +
                (if UserFieldList <> "" and v-field-list <> "" then ",":U else "") +
                v-field-list.
                
            v-cookie-list = GetCookie(?).
            v-value       = v-value +
                (if v-value <> "" and v-cookie-list <> "" then ",":U else "") +
                v-cookie-list.
            return v-value.
        end.
	
        /* Else, item name passed so look for it in user fields, fields and
           cookies in that order. */
        else 
        do:
            assign 
                i = lookup(p_name, UserFieldList).
            if i > 0 then
                return UserFieldVar[i].
	
            if m_usettWebFieldList then
                found = can-find (first ttWebFieldList where field-name = p_name
                /*AND (field-type = "F":U OR field-type = "Q":U)*/).
            else
                found = lookup(p_name, get-field(?)) > 0.
	    
            if found then
                return get-field(p_name).
	
            return GetCookie(p_name).
        end.
	
    end.
	
    /**
	
    see get-value()
    
    Input Parameter: Name of item or ?
    Returns: Value of user field, form field or Cookie in that order or blank if 
      an invalid name.  If ? was specified for the name, a comma separated list
    of all user fields, fields and cookies is returned.
    Global Variables: UserFieldList, UserFieldVar
	
    This is an enhanced version of get-value which handles long lists with 
    LONGCHAR
    */
	
    method public longchar get-valueEx 
        (input p_name as character) :
        define variable v-value       as longchar  no-undo.
        define variable v-field-list  as longchar  no-undo.
        define variable v-cookie-list as character no-undo.
        define variable i             as integer   no-undo.
        define variable found         as logical   no-undo.
	
        /* If name is ?, pass a list of all names in user fields, fields and
           cookies. */
        if p_name = ? then 
        do:
            v-field-list  = get-fieldEx(?).
            v-value       = UserFieldList +
                (if UserFieldList <> "" and v-field-list <> "" then ",":U else "") +
                v-field-list.
            v-cookie-list = GetCookie(?).
            v-value       = v-value +
                (if v-value <> "" and v-cookie-list <> "" then ",":U else "") +
                v-cookie-list.
            return v-value.
        end.
        /* Else, item name passed so look for it in user fields, fields and
           cookies in that order. */
        else 
        do:
            i = lookup(p_name, UserFieldList).
            if i > 0 then
                return UserFieldVar[i].
	
            if m_usettWebFieldList then
                found = can-find (first ttWebFieldList where field-name = p_name
                /*AND (field-type = "F":U OR field-type = "Q":U)*/).
            else
                found = lookup(p_name, get-fieldEx(?)) > 0.
	
            if found then
                return get-fieldEx(p_name).
	
            return GetCookie(p_name).
        end.
	
    end.

    /**
    Description: Given a cookie name, returns one or more matching values.  If 
                 more than one value matches, all are returned delimited by the 
                 value of SelDelim, normally a comma.  If ? is specified for the 
                 name, the output will be a list of all the cookie names.
    Input:       Name or ?
    Output:      Cookie value(s) or list of cookie names
    */
    method public character GetCookie
        (input p_name as character) :

        define variable v-values as character no-undo.
        define variable cookies  as character no-undo.
        define variable i-pair   as character no-undo.
        define variable i-name   as character no-undo.
        define variable i-value  as character no-undo.
        define variable ix       as integer   no-undo.

        /* Return if no HTTP Cookie: header */
        if this-object:HTTPCookie = "" then 
            return v-values.

        /* Loop through all the name=value pairs ... */
        do ix = 1 to num-entries(this-object:HTTPCookie, "~;":U):
                i-pair  = trim(entry(ix, this-object:HTTPCookie, "~;":U)).
                i-name  = this-object:WebContext:url-decode(entry(1, i-pair, "=":U)).
                i-value = (if num-entries(i-pair, "=":U) > 1 then
                   this-object:WebContext:url-decode(substring(i-pair,length(i-name,   "character":U) + 2, -1, "character":U)) 
                 else "?":U).
            /* If name = ?, just add name to list of names.  If a name is already on 
               the list, don't add again. */
            if p_name = ? then 
            do:
                if lookup(i-name, v-values) = 0 then
                    assign v-values = v-values +
          (if v-values = "" then "" else ",":U) + i-name.
            end.

            /* If a match, then add current value to list of values using configured 
               delimiter. */
            else
                if i-name = p_name then
                    assign v-values = v-values + 
          (if v-values = "" then "" else SelDelim) + i-value.
        end.

        return v-values.
    end .

end class.